package com.yuand.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.yuand.constant.Constant;
import com.yuand.utils.BodyReaderHttpServletRequestWrapper;
import com.yuand.utils.HttpUtils;
import com.yuand.utils.SignUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * @author yuandong
 * @date 2021/8/20 10:37
 */
@Component
@Slf4j
public class SignAuthFilter extends OncePerRequestFilter {

        /**
         * OncePerRequestFilter过滤器保证一次请求只调用一次doFilterInternal方法;如内部的forward不会再多执行一次
         * @param request
         * @param response
         * @param filterChain
         * @throws ServletException
         * @throws IOException
         */
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            //包装HttpServletRequest对象，缓存body数据，再次读取的时候将缓存的值写出,解决HttpServetRequest读取body只能一次的问题
            HttpServletRequest requestWrapper = null;
            if (request instanceof HttpServletRequest) {
                requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
            }

            //校验头部参数是否有效
            boolean isValid = SignUtils.verifyHeaderParams(requestWrapper);
            if (isValid) {
                //获取header中的参数
                SortedMap<String, String> headerParams = HttpUtils.getHeaderParams(requestWrapper);

                //根据调用传递的appId获取对应的appSecret（应用密钥）
                String url = headerParams.get(Constant.REQ_URL);
                String appKey = headerParams.get(Constant.APP_KEY);
                String nonce = headerParams.get(Constant.NONCE);
                long timestamp = Long.parseLong(headerParams.get(Constant.TIME_STAMP));
                String appSecret = getAppSecret(headerParams.get(Constant.APP_KEY));
                String requestBody = HttpUtils.getRequestBody(requestWrapper);
                LinkedHashMap<String,List<String>> params = HttpUtils.getUrlParams(requestWrapper);
                String sign_houtai="";
                try {
                    //根据要求生成签名
                    sign_houtai = SignUtils.signature(url, params, nonce, timestamp, requestBody, appKey, appSecret);
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                } catch (InvalidKeyException e) {
                    e.printStackTrace();
                }
                //appSecret（应用密钥）存在
                if (StringUtils.isNotEmpty(appSecret)) {
                    //将调用方应用id对应的应用密钥与请求参数合成指定
                    boolean isSigned = SignUtils.verifySignature(headerParams, sign_houtai);
                    if (isSigned) {
                        log.info("签名通过");
                        filterChain.doFilter(request, response);
                        return;
                    }
                }
            }
            log.info("签名参数校验出错");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json;charset=utf-8");

            PrintWriter out = response.getWriter();
            JSONObject resParam = new JSONObject();
            resParam.put("msg", "签名参数校验出错");
            resParam.put("success", "false");
            out.append(resParam.toJSONString());
        }

        /**
         * 打印请求信息
         * @param request
         */
        private void printRequest(HttpServletRequest request) {
            BodyReaderHttpServletRequestWrapper requestWrapper = null;
            if (request instanceof BodyReaderHttpServletRequestWrapper) {
                requestWrapper = (BodyReaderHttpServletRequestWrapper) request;
            }

            JSONObject requestJ = new JSONObject();
            JSONObject headers = new JSONObject();
            Collections.list(request.getHeaderNames())
                    .stream()
                    .forEach(name -> headers.put(name, request.getHeader(name)));
            requestJ.put("headers", headers);
            requestJ.put("parameters", request.getParameterMap());
            requestJ.put("body", requestWrapper.getBody());
            requestJ.put("remote-user", request.getRemoteUser());
            requestJ.put("remote-addr", request.getRemoteAddr());
            requestJ.put("remote-host", request.getRemoteHost());
            requestJ.put("remote-port", request.getRemotePort());
            requestJ.put("uri", request.getRequestURI());
            requestJ.put("url", request.getRequestURL());
            requestJ.put("servlet-path", request.getServletPath());
            requestJ.put("method", request.getMethod());
            requestJ.put("query", request.getQueryString());
            requestJ.put("path-info", request.getPathInfo());
            requestJ.put("context-path", request.getContextPath());
            log.info("Request-Info: " + JSON.toJSONString(requestJ, SerializerFeature.PrettyFormat));
        }


        /**
         * 获取appId对应的secret,假数据
         * @param appKey
         * @return
         */
        public String getAppSecret(String appKey) {
            Map<String, String> map = new HashMap<>();
            map.put("9aON8USr", "VHc1CBWM0YS204rydm+wWxkQrqJSVyXAAgFRktVpOF4=");
            return map.get(appKey);
        }

}
